Interpolator.point   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
1
/*
2
 * Copyright (c) 2019 Rafael da Silva Rocha.
3
 * Copyright 2012 Spencer Cohen
4
 *
5
 * Permission is hereby granted, free of charge, to any person obtaining
6
 * a copy of this software and associated documentation files (the
7
 * "Software"), to deal in the Software without restriction, including
8
 * without limitation the rights to use, copy, modify, merge, publish,
9
 * distribute, sublicense, and/or sell copies of the Software, and to
10
 * permit persons to whom the Software is furnished to do so, subject to
11
 * the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be
14
 * included in all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
 *
24
 */
25
26
/**
27
 * @fileoverview The Interpolator class. Based on Smooth.js by Spencer Cohen.
28
 * @see https://github.com/rochars/wave-resampler
29
 * @see https://github.com/osuushi/Smooth.js
30
 */
31
32
/**
33
 * A class to get scaled values out of arrays.
34
 */
35
export class Interpolator {
36
  
37
  /**
38
   * @param {number} scaleFrom the length of the original array.
39
   * @param {number} scaleTo The length of the new array.
40
   * @param {?Object} details The extra configuration, if needed.
41
   */
42
  constructor(scaleFrom, scaleTo, details) {
43
    /**
44
     * The length of the original array.
45
     * @type {number}
46
     */
47
    this.length_ = scaleFrom;
48
    /**
49
     * The scaling factor.
50
     * @type {number}
51
     */
52
    this.scaleFactor_ = (scaleFrom - 1) / scaleTo;
53
    /**
54
     * The interpolation function.
55
     * @type {Function}
56
     */
57
    this.interpolate = this.cubic;
58
    if (details.method === 'point') {
59
    	this.interpolate = this.point;
60
    } else if(details.method === 'linear') {
61
    	this.interpolate = this.linear;
62
    } else if(details.method === 'sinc') {
63
    	this.interpolate = this.sinc;
64
    }
65
    /**
66
     * The tanget factor for cubic interpolation.
67
     * @type {number}
68
     */
69
    this.tangentFactor_ = 1 - Math.max(0, Math.min(1, details.tension || 0));
70
    // Configure the kernel for sinc
71
    /**
72
     * The sinc filter size.
73
     * @type {number}
74
     */
75
    this.sincFilterSize_ = details.sincFilterSize || 1;
76
    /**
77
     * The sinc kernel.
78
     * @type {Function}
79
     */
80
    this.kernel_ = sincKernel_(details.sincWindow || window_);
81
  }
82
83
  /**
84
   * @param {number} t The index to interpolate.
85
   * @param {Array|TypedArray} samples the original array.
86
   * @return {number} The interpolated value.
87
   */
88
  point(t, samples) {
89
    return this.getClippedInput_(Math.round(this.scaleFactor_ * t), samples);
90
  }
91
92
  /**
93
   * @param {number} t The index to interpolate.
94
   * @param {Array|TypedArray} samples the original array.
95
   * @return {number} The interpolated value.
96
   */
97
  linear(t, samples) {
98
    t = this.scaleFactor_ * t;
99
    let k = Math.floor(t);
100
    t -= k;
101
    return (1 - t) *
102
    	this.getClippedInput_(k, samples) + t *
103
    	this.getClippedInput_(k + 1, samples);
104
  }
105
106
  /**
107
   * @param {number} t The index to interpolate.
108
   * @param {Array|TypedArray} samples the original array.
109
   * @return {number} The interpolated value.
110
   */
111
  cubic(t, samples) {
112
    t = this.scaleFactor_ * t;
113
    let k = Math.floor(t);
114
    let m = [this.getTangent_(k, samples), this.getTangent_(k + 1, samples)];
115
    let p = [this.getClippedInput_(k, samples),
116
      this.getClippedInput_(k + 1, samples)];
117
    t -= k;
118
    let t2 = t * t;
119
    let t3 = t * t2;
120
    return (2 * t3 - 3 * t2 + 1) *
121
      p[0] + (t3 - 2 * t2 + t) *
122
      m[0] + (-2 * t3 + 3 * t2) *
123
      p[1] + (t3 - t2) * m[1];
124
  }
125
126
  /**
127
   * @param {number} t The index to interpolate.
128
   * @param {Array|TypedArray} samples the original array.
129
   * @return {number} The interpolated value.
130
   */
131
  sinc(t, samples) {
132
    t = this.scaleFactor_ * t;
133
    let k = Math.floor(t);
134
    let ref = k - this.sincFilterSize_ + 1;
135
    let ref1 = k + this.sincFilterSize_;
136
    let sum = 0;
137
    for (let n = ref; n <= ref1; n++) {
138
      sum += this.kernel_(t - n) * this.getClippedInput_(n, samples);
139
    }
140
    return sum;
141
  }
142
143
  /**
144
   * @param {number} k The scaled index to interpolate.
145
   * @param {Array|TypedArray} samples the original array.
146
   * @return {number} The tangent.
147
   * @private
148
   */
149
  getTangent_(k, samples) {
150
    return this.tangentFactor_ *
151
      (this.getClippedInput_(k + 1, samples) -
152
        this.getClippedInput_(k - 1, samples)) / 2;
153
  }
154
155
  /**
156
   * @param {number} t The scaled index to interpolate.
157
   * @param {Array|TypedArray} samples the original array.
158
   * @return {number} The interpolated value.
159
   * @private
160
   */
161
  getClippedInput_(t, samples) {
162
    if ((0 <= t && t < this.length_)) {
163
      return samples[t];
164
    }
165
    return 0;
166
  }
167
}
168
169
// Sinc functions
170
171
/**
172
 * The default window function.
173
 * @param {number} x The sinc signal.
174
 * @return {number}
175
 * @private
176
 */
177
function window_(x) {
178
  return Math.exp(-x / 2 * x / 2);
179
}
180
181
/**
182
 * @param {Function} window The window function.
183
 * @return {Function}
184
 * @private
185
 */
186
function sincKernel_(window) {
187
  return function(x) { return sinc_(x) * window(x); };
188
}
189
190
/**
191
 * @param {number} x The sinc signal.
192
 * @return {number}
193
 * @private
194
 */
195
function sinc_(x) {
196
  if (x === 0) {
197
    return 1;
198
  }
199
  return Math.sin(Math.PI * x) / (Math.PI * x);
200
}
201
202